﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Web;
using Sausage.Common;
using Sausage.Web.Code;
using Sausage.Web.DataModel;
using Sausage.Web.Models;
using Telerik.Web.Mvc;

namespace Sausage.Web.Domain
{
    public class ProjectPlanHelper
    {
        public static GridModel<object> Select(JosnPagingQueryModel query)
        {
            Guid targetID;
            var isWithChild = Helper.IsNeedOrganizationQuery(query, out targetID);
            List<Guid> organization = OrganizationHelper.GetAllowedOrganizationList(isWithChild, targetID); 
            
            using (var context = new SausageEntities())
            {
                return
                    QueryProjectPlan(
                        context.ProjectPlan.Where(
                            item =>
                            organization.Contains(item.OrganizationID) &&
                            (item.State == (int)ProjectDataStateEnum.Confirmed)),
                        query, isWithChild);
            }
        }

        public static GridModel<object> SubmitSelect(JosnPagingQueryModel query)
        {
            Guid targetID;
            var isWithChild = Helper.IsNeedOrganizationQuery(query, out targetID);
            List<Guid> organization = OrganizationHelper.GetAllowedOrganizationList(isWithChild, targetID);
            
            using (var context = new SausageEntities())
            {
                return
                    QueryProjectPlan(
                        context.ProjectPlan.Where(
                            item =>
                            organization.Contains(item.OrganizationID) &&
                            (item.State == (int)ProjectDataStateEnum.Unsubmit ||
                             item.State == (int)ProjectDataStateEnum.AuditRejected)),
                        query, isWithChild);
            }
        }

        public static GridModel<object> AuditSelect(JosnPagingQueryModel query)
        {
            Guid targetID;
            var isWithChild = Helper.IsNeedOrganizationQuery(query, out targetID);
            List<Guid> organization = OrganizationHelper.GetAllowedOrganizationList(isWithChild, targetID);
            
            using (var context = new SausageEntities())
            {
                return
                    QueryProjectPlan(
                        context.ProjectPlan.Where(
                            item =>
                            organization.Contains(item.OrganizationID) &&
                            (item.State == (int)ProjectDataStateEnum.Submitted ||
                             item.State == (int)ProjectDataStateEnum.SignRejected)),
                        query, isWithChild);
            }
        }

        public static GridModel<object> SignSelect(JosnPagingQueryModel query)
        {
            Guid targetID;
            var isWithChild = Helper.IsNeedOrganizationQuery(query, out targetID);
            List<Guid> organization = OrganizationHelper.GetAllowedOrganizationList(isWithChild, targetID);
            
            using (var context = new SausageEntities())
            {
                return
                    QueryProjectPlan(
                        context.ProjectPlan.Where(
                            item =>
                            organization.Contains(item.OrganizationID) &&
                            (item.State == (int)ProjectDataStateEnum.Audited ||
                             item.State == (int)ProjectDataStateEnum.ConfirmationRejected)),
                        query, isWithChild);
            }
        }

        public static GridModel<object> ConfirmSelect(JosnPagingQueryModel query)
        {
            Guid targetID;
            var isWithChild = Helper.IsNeedOrganizationQuery(query, out targetID);
            List<Guid> organization = OrganizationHelper.GetAllowedOrganizationList(isWithChild, targetID);
            
            using (var context = new SausageEntities())
            {
                return
                    QueryProjectPlan(context.ProjectPlan.Where(item =>
                                                               organization.Contains(item.OrganizationID) &&
                                                               item.State == (int)ProjectDataStateEnum.Signed)
                                     , query, isWithChild);
            }
        }

        private static GridModel<object> QueryProjectPlan(IQueryable<ProjectPlan> source, JosnPagingQueryModel query, bool isWithChild)
        {
            var result = UIHelper.GetGridModelFromQueryModel<ProjectPlan>(
                source,
                query.page,
                query.limit,
                GetSortDescriptorFromQueryModel(query.SortModel),
                GetSearchFilterFromQueryModel(query.FilterModel, isWithChild));

            var list = result.Data.Select(item => new
                                                      {
                                                          item.PlanID,
                                                          item.OrganizationID,
                                                          item.Version,
                                                          Organization = item.Organization.OrganizationName,
                                                          item.ProjectID,
                                                          item.Project.ProjectName,
                                                          item.Project.ProjectCode,
                                                          item.Applicant,
                                                          item.ApplicationDate,
                                                          item.Amount,
                                                          item.Auditor,
                                                          item.AuditedDate,
                                                          item.Approver,
                                                          item.ApprovedDate,
                                                          item.Signer,
                                                          item.SignedDate,
                                                          item.State,
                                                          item.Memo,
                                                          item.Comment
                                                      }).ToList();

            return new GridModel<object>()
            {
                Aggregates = result.Aggregates,
                Data = list,
                Total = result.Total
            };
        }

        private static List<IFilterDescriptor> GetSearchFilterFromQueryModel(IEnumerable<JosnQueryFilterModel> filterModels, bool isWithChild)
        {
            var filterList = new List<IFilterDescriptor>();
            foreach (var item in filterModels)
            {
                if (string.IsNullOrEmpty(item.value))
                    continue;

                if (item.property == "OrganizationID" && isWithChild)
                    continue;

                if (item.property.EndsWith("ID", StringComparison.InvariantCultureIgnoreCase))
                {
                    var targetID = Guid.Parse(item.value);
                    if (targetID != Guid.Empty)
                        filterList.Add(new FilterDescriptor(item.property, FilterOperator.IsEqualTo, targetID));
                }
                else if (item.property == "ApplicationDate_L") //startData - begin
                {
                    var targetDate = DateTime.Parse(item.value);
                    filterList.Add(new FilterDescriptor("ApplicationDate", FilterOperator.IsGreaterThanOrEqualTo, targetDate));
                }
                else if (item.property == "ApplicationDate_H") // startDate - end.
                {
                    var targetDate = DateTime.Parse(item.value);
                    filterList.Add(new FilterDescriptor("ApplicationDate", FilterOperator.IsLessThanOrEqualTo, targetDate));
                }
                else //other filter.
                    filterList.Add(new FilterDescriptor(item.property, FilterOperator.Contains, item.value));
            }
            return filterList;
        }

        private static List<SortDescriptor> GetSortDescriptorFromQueryModel(IEnumerable<JosnQuerySortModel> sortModels)
        {
            //convert the sort description.
            var sortList = new List<SortDescriptor>();
            foreach (var item in sortModels)
            {
                var descriptor = new SortDescriptor()
                {
                    Member = item.property,
                    SortDirection =
                        item.direction.ToLower() == "desc"
                            ? ListSortDirection.Descending
                            : ListSortDirection.Ascending
                };

                if (descriptor.Member == "Organization")
                    descriptor.Member = "Organization.OrganizationName";

                sortList.Add(descriptor);
            }
            return sortList;
        }

        public static ProjectPlanListRow GetProjectPlanByID(Guid id)
        {
            using (var context = new SausageEntities())
            {
                var data =
                    context.ProjectPlan.Include("Organization").Include("Items").Include("Items.Construction").
                        FirstOrDefault(item => item.PlanID == id);

                if (data == null)
                    return null;

                var result = new ProjectPlanListRow();
                result.PlanID = data.PlanID;
                result.Version = data.Version;
                result.OrganizationID = data.OrganizationID;
                result.Organization = data.Organization.OrganizationName;
                result.ProjectID = data.ProjectID;
                result.ProjectName = data.Project.ProjectName;
                result.ProjectCode = data.Project.ProjectCode;
                result.Applicant = data.Applicant;
                result.ApplicationDate = data.ApplicationDate;
                result.Auditor = data.Auditor;
                result.AuditedDate = data.AuditedDate;
                result.Approver = data.Approver;
                result.ApprovedDate = data.ApprovedDate;
                result.State = data.State;
                result.Memo = data.Memo;

                var list = new List<ProjectPlanItemListRow>();
                foreach (var item in data.Items)
                {
                    list.Add(new ProjectPlanItemListRow()
                                 {
                                     PlanID = data.PlanID,
                                     PlanItemID = item.PlanItemID,
                                     ConstructionID = item.ConstructionID,
                                     Construction = item.Construction.ConstructionName,
                                     Quantity = item.Quantity,
                                     Price = item.Price,
                                     Amount = item.Amount,
                                     Unit = item.Construction.Unit
                                 });
                }

                result.Items = list.ToArray();
                return result;
            }
        }

        public static void CreateProjectPlan(ProjectPlanListRow data)
        {
            using (var context = new SausageEntities())
            {
                var projectPlan = new ProjectPlan();
                projectPlan.PlanID = Guid.NewGuid();
                projectPlan.OrganizationID = Helper.CurrentPrincipal.User.OrganizationID;
                projectPlan.ProjectID = data.ProjectID;
                projectPlan.Amount = data.Items.Sum(item => item.Quantity * item.Price);
                projectPlan.Applicant = Helper.CurrentPrincipal.User.UserName;
                projectPlan.ApplicationDate = DateTime.Now;
                projectPlan.State = (int)MaintainDataStateEnum.Unsubmit;
                projectPlan.Memo = data.Memo;
                projectPlan.Version = 1; //The new project plan.
                context.AddToProjectPlan(projectPlan);

                foreach (var item in data.Items)
                {
                    var projectPlanItem = new ProjectPlanItem();
                    projectPlanItem.PlanID = projectPlan.PlanID;
                    projectPlanItem.PlanItemID = Guid.NewGuid();
                    projectPlanItem.ConstructionID = item.ConstructionID;
                    projectPlanItem.Quantity = item.Quantity;
                    projectPlanItem.Price = item.Price;
                    projectPlanItem.Amount = item.Quantity * item.Price;
                    context.AddToProjectPlanItem(projectPlanItem);
                }

                context.SaveChanges();
            }
        }

        public static void DeleteProjectPlan(Guid planID)
        {
            using (var context = new SausageEntities())
            {
                //Delete the Project Plan Item
                var list = context.ProjectPlanItem.Where(item => item.PlanID == planID).ToList();
                foreach (var item in list)
                    context.DeleteObject(item);

                //Delete the Project Plan
                var projectPlan = new ProjectPlan { PlanID = planID };
                context.CreateObjectSet<ProjectPlan>().Attach(projectPlan);
                context.DeleteObject(projectPlan);
                context.SaveChanges();
            }
        }

        public static void UpdateProjectPlan(ProjectPlanListRow data)
        {
            using (var context = new SausageEntities())
            {
                var target = context.ProjectPlan.FirstOrDefault(item => item.PlanID == data.PlanID);
                if (target == null)
                    return;

                if (target.State != (int)ProjectDataStateEnum.Unsubmit && target.State != (int)ProjectDataStateEnum.AuditRejected)
                    throw new SausageException(string.Format("项目工程计划已经提交，不允许执行编辑操作。"));


                target.OrganizationID = Helper.CurrentPrincipal.User.OrganizationID;
                target.Applicant = Helper.CurrentPrincipal.User.UserName;
                target.ApplicationDate = DateTime.Now;
                target.Amount = data.Items.Sum(item => item.Quantity * item.Price);
                target.Memo = data.Memo;

                //delete all the item.
                var list = context.ProjectPlanItem.Where(item => item.PlanID == data.PlanID).ToList();
                foreach (var item in list)
                {
                    context.CreateObjectSet<ProjectPlanItem>().Attach(item);
                    context.ObjectStateManager.ChangeObjectState(item, EntityState.Deleted);
                }

                //insert the new item.
                foreach (var item in data.Items)
                {
                    var projectPlanItem = new ProjectPlanItem();
                    projectPlanItem.PlanID = data.PlanID;
                    projectPlanItem.PlanItemID = Guid.NewGuid();
                    projectPlanItem.ConstructionID = item.ConstructionID;
                    projectPlanItem.Quantity = item.Quantity;
                    projectPlanItem.Price = item.Price;
                    projectPlanItem.Amount = item.Quantity * item.Price;
                    context.AddToProjectPlanItem(projectPlanItem);
                }

                context.SaveChanges();
            }
        }

        public static void UpdateToNewProjectPlan(ProjectPlanListRow data)
        {
            using (var context = new SausageEntities())
            {
                // 工程计划 Update To New.
                // 完全插入一版本新的. 并且 Version++.
                var target =
                    context.ProjectPlan.Where(item => item.ProjectID == data.ProjectID).OrderByDescending(
                        item => item.Version).FirstOrDefault();

                if (target == null)
                    return;

                var newVersion = target.Version + 1; //new item the version++;

                var projectPlan = new ProjectPlan();
                projectPlan.PlanID = Guid.NewGuid();
                projectPlan.Amount = data.Items.Sum(item => item.Quantity * item.Price);
                projectPlan.OrganizationID = Helper.CurrentPrincipal.User.OrganizationID;
                projectPlan.ProjectID = data.ProjectID;
                projectPlan.Applicant = Helper.CurrentPrincipal.User.UserName;
                projectPlan.ApplicationDate = DateTime.Now;
                projectPlan.State = (int)MaintainDataStateEnum.Unsubmit;
                projectPlan.Memo = data.Memo;
                projectPlan.Version = newVersion;
                context.AddToProjectPlan(projectPlan);

                foreach (var item in data.Items)
                {
                    var projectPlanItem = new ProjectPlanItem();
                    projectPlanItem.PlanID = projectPlan.PlanID;
                    projectPlanItem.PlanItemID = Guid.NewGuid();
                    projectPlanItem.ConstructionID = item.ConstructionID;
                    projectPlanItem.Quantity = item.Quantity;
                    projectPlanItem.Price = item.Price;
                    projectPlanItem.Amount = item.Quantity * item.Price;
                    context.AddToProjectPlanItem(projectPlanItem);
                }

                context.SaveChanges();
            }
        }

        public static void UpdateProjectPlanState(Guid id, int state)
        {
            using (var context = new SausageEntities())
            {
                var projectPlan = context.ProjectPlan.FirstOrDefault(item => item.PlanID == id);
                if (projectPlan == null)
                    return;

                projectPlan.State = state;
                context.SaveChanges();
            }
        }

        public static void ExecuteSubmit(Guid[] plans)
        {
            if (null != plans && plans.Length > 0)
            {
                using (var context = new SausageEntities())
                {
                    var list = context.ProjectPlan.Where(item => plans.Contains(item.PlanID)).ToList();

                    foreach (var projectPlan in list)
                    {
                        projectPlan.State = (int)ProjectDataStateEnum.Submitted;
                    }
                    context.SaveChanges();
                }
            }
        }

        public static void ExecuteAudit(Guid[] plans, bool passed, string comment)
        {
            if (null != plans && plans.Length > 0)
            {
                using (var context = new SausageEntities())
                {
                    var list = context.ProjectPlan.Where(item => plans.Contains(item.PlanID)).ToList();

                    foreach (var projectPlan in list)
                    {
                        projectPlan.Auditor = Helper.CurrentPrincipal.User.UserName;
                        projectPlan.AuditedDate = DateTime.Now;
                        projectPlan.Comment = comment;
                        projectPlan.State = passed
                                                     ? (int)ProjectDataStateEnum.Audited
                                                     : (int)ProjectDataStateEnum.AuditRejected;
                    }
                    context.SaveChanges();
                }
            }
        }

        public static void ExecuteSign(Guid[] plans, bool passed)
        {
            if (null != plans && plans.Length > 0)
            {
                using (var context = new SausageEntities())
                {
                    var list = context.ProjectPlan.Where(item => plans.Contains(item.PlanID)).ToList();

                    foreach (var projectPlan in list)
                    {
                        projectPlan.Signer = Helper.CurrentPrincipal.User.UserName;
                        projectPlan.SignedDate = DateTime.Now;
                        projectPlan.State = passed
                                                     ? (int)ProjectDataStateEnum.Signed
                                                     : (int)ProjectDataStateEnum.SignRejected;
                    }
                    context.SaveChanges();
                }
            }
        }

        public static void ExecuteConfirm(Guid[] plans, bool passed, string comment)
        {
            if (null != plans && plans.Length > 0)
            {
                using (var context = new SausageEntities())
                {
                    var list = context.ProjectPlan.Where(item => plans.Contains(item.PlanID)).ToList();

                    foreach (var projectPlan in list)
                    {
                        projectPlan.Approver = Helper.CurrentPrincipal.User.UserName;
                        projectPlan.ApprovedDate = DateTime.Now;
                        projectPlan.Comment = comment;
                        projectPlan.State = passed
                                                     ? (int)ProjectDataStateEnum.Confirmed
                                                     : (int)ProjectDataStateEnum.ConfirmationRejected;
                    }
                    context.SaveChanges();
                }
            }
        }

        public static ProjectPlan GetRawProjectPlanByProjectID(Guid projectID)
        {
            using (SausageEntities context = new SausageEntities())
            {
                return context.ProjectPlan.Where(item => item.ProjectID == projectID).OrderByDescending(
                    item => item.Version).FirstOrDefault();
            }
        }

        public static ProjectPlanListRow GetProjectPlanByProjectID(Guid projectID)
        {
            using (var context = new SausageEntities())
            {
                //查询版本号最大的且已经批准的工程计划
                var data =
                    context.ProjectPlan.Include("Organization").Include("Items").Include("Items.Construction").Where(
                        item => (item.ProjectID == projectID) && item.State == (int)ProjectDataStateEnum.Confirmed).
                        OrderByDescending(item => item.Version).FirstOrDefault();

                if (data == null)
                    return null;

                var result = new ProjectPlanListRow();
                result.PlanID = data.PlanID;
                result.Version = data.Version;
                result.OrganizationID = data.OrganizationID;
                result.Organization = data.Organization.OrganizationName;
                result.ProjectID = data.ProjectID;
                result.ProjectName = data.Project.ProjectName;
                result.ProjectCode = data.Project.ProjectCode;
                result.Applicant = data.Applicant;
                result.ApplicationDate = data.ApplicationDate;
                result.Amount = data.Amount;
                result.Auditor = data.Auditor;
                result.AuditedDate = data.AuditedDate;
                result.Approver = data.Approver;
                result.ApprovedDate = data.ApprovedDate;
                result.State = data.State;
                result.Memo = data.Memo;

                var list = new List<ProjectPlanItemListRow>();
                foreach (var item in data.Items)
                {
                    list.Add(new ProjectPlanItemListRow()
                    {
                        PlanID = data.PlanID,
                        PlanItemID = item.PlanItemID,
                        ConstructionID = item.ConstructionID,
                        Construction = item.Construction.ConstructionName,
                        Quantity = item.Quantity,
                        Price = item.Price,
                        Amount = item.Amount,
                        Unit = item.Construction.Unit
                    });
                }

                result.Items = list.ToArray();
                return result;
            }
        }

        public static ProjectPlanListRow GetAllProjectPlanByProjectID(Guid projectID)
        {
            using (var context = new SausageEntities())
            {
                //查询版本号最大的且已经批准的工程计划
                var data =
                    context.ProjectPlan.Include("Organization").Include("Items").Include("Items.Construction").Where(
                        item => (item.ProjectID == projectID)).
                        OrderByDescending(item => item.Version).FirstOrDefault();

                if (data == null)
                    return null;

                var result = new ProjectPlanListRow();
                result.PlanID = data.PlanID;
                result.OrganizationID = data.OrganizationID;
                result.Organization = data.Organization.OrganizationName;
                result.ProjectID = data.ProjectID;
                result.ProjectName = data.Project.ProjectName;
                result.ProjectCode = data.Project.ProjectCode;
                result.Applicant = data.Applicant;
                result.ApplicationDate = data.ApplicationDate;
                result.Amount = data.Amount;
                result.Auditor = data.Auditor;
                result.AuditedDate = data.AuditedDate;
                result.Approver = data.Approver;
                result.ApprovedDate = data.ApprovedDate;
                result.State = data.State;
                result.Memo = data.Memo;

                var list = new List<ProjectPlanItemListRow>();
                foreach (var item in data.Items)
                {
                    list.Add(new ProjectPlanItemListRow()
                    {
                        PlanID = data.PlanID,
                        PlanItemID = item.PlanItemID,
                        ConstructionID = item.ConstructionID,
                        Construction = item.Construction.ConstructionName,
                        Quantity = item.Quantity,
                        Price = item.Price,
                        Amount = item.Amount,
                        Unit = item.Construction.Unit
                    });
                }

                result.Items = list.ToArray();
                return result;
            }
        }

        public static List<ProjectPlanListRow> GetProjectPlanListByProjectID(Guid projectID)
        {
            using (var context = new SausageEntities())
            {
                var list =
                    context.ProjectPlan.Include("Organization").Include("Items").Include("Items.Construction").Where(
                        item => (item.ProjectID == projectID) && item.State == (int)ProjectDataStateEnum.Confirmed).
                        OrderByDescending(item => item.Version).ToList();

                var result = new List<ProjectPlanListRow>();
                foreach (var data in list)
                {
                    var projectPlanListRow = new ProjectPlanListRow();
                    projectPlanListRow.PlanID = data.PlanID;
                    projectPlanListRow.OrganizationID = data.OrganizationID;
                    projectPlanListRow.Organization = data.Organization.OrganizationName;
                    projectPlanListRow.ProjectID = data.ProjectID;
                    projectPlanListRow.ProjectName = data.Project.ProjectName;
                    projectPlanListRow.ProjectCode = data.Project.ProjectCode;
                    projectPlanListRow.Applicant = data.Applicant;
                    projectPlanListRow.Amount = data.Amount;
                    projectPlanListRow.ApplicationDate = data.ApplicationDate;
                    projectPlanListRow.Auditor = data.Auditor;
                    projectPlanListRow.AuditedDate = data.AuditedDate;
                    projectPlanListRow.Approver = data.Approver;
                    projectPlanListRow.ApprovedDate = data.ApprovedDate;
                    projectPlanListRow.State = data.State;
                    projectPlanListRow.Memo = data.Memo;

                    var itemList = new List<ProjectPlanItemListRow>();
                    foreach (var item in data.Items)
                    {
                        itemList.Add(new ProjectPlanItemListRow()
                                         {
                                             PlanID = data.PlanID,
                                             PlanItemID = item.PlanItemID,
                                             ConstructionID = item.ConstructionID,
                                             Construction = item.Construction.ConstructionName,
                                             Quantity = item.Quantity,
                                             Price = item.Price,
                                             Amount = item.Amount,
                                             Unit = item.Construction.Unit
                                         });
                    }

                    projectPlanListRow.Items = itemList.ToArray();
                    result.Add(projectPlanListRow);
                }

                return result;
            }
        }

        public static bool ProjectHasPlan(Guid projectID)
        {
            using (var context = new SausageEntities())
            {
                return context.ProjectPlan.Any(item => (item.ProjectID == projectID));
            }
        }

        public static bool ProjectHasConfirmedPlan(Guid projectID)
        {
            using (var context = new SausageEntities())
            {
                return context.ProjectPlan.Any(item => (item.ProjectID == projectID && item.State != (int)ProjectDataStateEnum.Confirmed));
            }
        }

    }
}